Skip to content

Conversation

@patulus
Copy link
Member

@patulus patulus commented Sep 9, 2025

What is this PR?🔍

  • 기본 이메일 주소 인증 여부가 인증 상태였으나 이를 비인증으로 변경합니다.
  • 이메일 주소 인증 시 인증 여부가 인증 상태로 변경되도록 합니다.

Changes💻

  • 데이터베이스 테이블과 매핑되는 Member 엔티티 생성 시 인증 여부를 기본적으로 비인증 상태로 설정합니다.
  • CompanyMemberService, ForeignerMemberService에서 회원가입 시 인증 여부를 건드리지 않도록 하여 비인증 상태가 유지되도록 합니다.
  • AuthEmailService에서 인증 코드를 입력하면 해당 인증 코드가 발급된 이메일 주소의 회원의 인증 여부를 인증 상태가 되도록 합니다.

@patulus patulus requested review from Juhye0k and kon28289 September 9, 2025 05:38
@patulus patulus self-assigned this Sep 9, 2025
@patulus patulus added the enhancement New feature or request label Sep 9, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 9, 2025

Summary by CodeRabbit

  • 신기능
    • 회원가입 시 이메일 인증 절차를 적용하여, 인증 코드 발송 및 검증을 통해 계정 활성화가 이루어집니다.
    • 신규 가입자는 기본적으로 미인증 상태로 시작하며, 인증 완료 시 계정이 활성화됩니다.
  • 버그 수정
    • 이미 인증된 이메일에 대한 재인증 시도를 차단하여 중복 처리 및 혼선을 예방했습니다.
    • 인증 완료 후 사용된 인증 코드를 즉시 제거해 보안성과 안정성을 강화했습니다.
  • 작업
    • 리뷰/자동화 도구 설정을 업데이트하여 개발자 경험과 검토 품질을 개선했습니다.

Walkthrough

이 PR은 이메일 인증 서비스가 이메일로 회원을 조회해 인증 상태를 갱신하도록 변경하고, Member의 기본 isVerified 값을 false로 수정했으며 Company 회원 가입 흐름에서 명시적 unsetVerified 호출을 제거했습니다. 또한 서브모듈 포인터와 .coderabbit.yaml 리뷰/도구 설정을 갱신합니다.

Changes

Cohort / File(s) Summary
이메일 인증 서비스
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
MemberRepository 주입 추가; sendMail에서 이메일 기반 Member 조회 후 부재 시 예외; 이미 검증된 경우 예외; 인증코드 생성·저장·전송 유지; verifyEmail에서 코드 검증 후 이메일로 Member 조회·검증(true) 처리 및 사용된 코드 삭제
회원 도메인·가입 흐름
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/domain/Member.java, src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java
Member.isVerified 기본값 truefalse 변경; CompanyMemberService.signUp에서 unsetVerified() 호출 제거(가입 시 명시적 미인증 제거)
리소스 서브모듈 포인터
src/main/resources/properties
서브프로젝트 커밋 포인터 업데이트 (11b82a2a… → 38ec497…)
리포지토리 리뷰/도구 설정
.coderabbit.yaml
리뷰·경로·도구·knowledge_base 관련 설정 대규모 추가/재구성(톤 지침, 경로별 지침, tools 확장, knowledge_base 옵션 등)

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User as 사용자
    participant AES as AuthEmailService
    participant ACS as AuthCodeRepository
    participant MR as MemberRepository
    participant M as Member

    User->>AES: verifyEmail(email, code)
    AES->>ACS: 인증코드 조회/검증
    ACS-->>AES: 코드 유효 여부
    alt 코드 유효
        AES->>MR: findByEmail(email)
        MR-->>AES: Member 또는 없음
        alt Member 존재
            AES->>M: setVerified(true)
            AES->>ACS: 사용된 인증코드 삭제
            AES-->>User: 성공 응답
        else Member 없음
            AES-->>User: 오류 (MEMBER_NOT_FOUND)
        end
    else 코드 무효
        AES-->>User: 오류 (INVALID_CODE 등)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks (1 passed, 2 warnings)

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning 현재 PR 설명에는 “What is this PR?”과 “Changes” 섹션이 잘 작성되어 있으나, 리포지토리의 설명 템플릿에 명시된 “ScreenShot📷” 섹션이 누락되어 있어 템플릿 구조를 완전히 따르지 못하고 있습니다. “ScreenShot📷” 섹션을 추가하여 관련 스크린샷을 첨부하거나, UI 변경이 없음을 명시하는 주석을 작성하여 템플릿 요구사항을 충족해 주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed 현재 PR 제목은 주요 변경사항인 이메일 기본 인증 상태를 “비인증”으로 설정하는 점을 명확하고 간결하게 요약하고 있어 변경 사항의 핵심을 잘 반영합니다.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/verify-email

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/domain/Member.java (1)

128-130: 오토언박싱 NPE 방지용 안전 getter로 변경 권장.

DB에 과거 null 데이터가 남아있을 경우를 대비해 안전 비교를 사용하세요.

-    public boolean isVerified() {
-        return isVerified;
-    }
+    public boolean isVerified() {
+        return Boolean.TRUE.equals(isVerified);
+    }
🧹 Nitpick comments (13)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/domain/Member.java (4)

66-68: 기본값 false 전환은 적절합니다. DB 스키마 기본값/제약도 함께 맞춰주세요.

애플리케이션 기본값만 바꾸면, DB 기본값과 불일치 시(직접 INSERT, 배치, ETL 등) 오동작 위험이 있습니다. 마이그레이션으로 NOT NULL + DEFAULT false를 보장해 주세요.

  • MySQL 예: ALTER TABLE member MODIFY COLUMN verified TINYINT(1) NOT NULL DEFAULT 0;
  • PostgreSQL 예:
    • ALTER TABLE member ALTER COLUMN verified SET DEFAULT FALSE;
    • ALTER TABLE member ALTER COLUMN verified SET NOT NULL;
    • 필요 시 UPDATE member SET verified = FALSE WHERE verified IS NULL;

116-118: 이메일 변경 시 인증 상태를 미인증으로 초기화 + 중복 로직 정리 제안.

이메일이 바뀌면 재인증 요구가 자연스럽습니다. 또한 updateMemberProfile에서 직접 필드 대입 대신 changeEmail을 재사용하면 중복이 줄고 일관성이 좋아집니다. 비즈니스 정책과 일치하는지 확인 부탁드립니다.

@@
-    public void changeEmail(String email) {
-        this.email = email;
-    }
+    public void changeEmail(String email) {
+        if (!this.email.equals(email)) {
+            this.email = email;
+            this.isVerified = false;
+        }
+    }
@@
-        this.email = request.email();
+        changeEmail(request.email());

Also applies to: 132-140


3-3: 코딩 컨벤션: jakarta.persistence.* 와일드카드 import 지양.

단일 클래스 import로 바꿔 주세요.

-import jakarta.persistence.*;
+import jakarta.persistence.AttributeOverride;
+import jakarta.persistence.AttributeOverrides;
+import jakarta.persistence.Column;
+import jakarta.persistence.Convert;
+import jakarta.persistence.Embedded;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;

110-111: 어노테이션 파라미터 공백 스타일 일치.

프로젝트 컨벤션에 맞춰 공백을 맞추면 가독성이 좋아집니다.

-    @OneToMany(mappedBy="member", fetch=FetchType.LAZY)
+    @OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
src/main/resources/properties (4)

1-1: DB 기본값·데이터 마이그레이션 필수 확인

코드에서 기본값을 false로 바꿔도 DB 컬럼 기본값이 true/NULL이면 불일치가 납니다. 다음 수행을 권장합니다.

  • DDL: members.is_verified DEFAULT false, NOT NULL 제약 확인/추가.
  • (선택) verified_at 타임스탬프 컬럼 추가.
  • 마이그레이션 전략 결정: 기존 회원을 “인증된 것으로 간주”할지, 초기 로그인 시 재인증 강제할지.
  • 인덱스/쿼리 영향 점검: is_verified 필터가 추가되는 조회에 보조 인덱스 필요 여부 평가.

1-1: 이메일 인증 트랜잭션·아이템포턴시·만료 처리 점검

verifyEmail 구현에서 다음을 반드시 보장해 주세요.

  • 트랜잭션 경계: 코드 검증→회원 로드→verified 설정→코드 삭제까지 단일 트랜잭션.
  • 아이템포턴시: 동일 코드 재요청 시 안전(두 번째 호출은 no-op/409 처리).
  • 만료/횟수 제한: 코드 TTL과 실패 횟수 제한, 브루트포싱 방지(지수 백오프/락).
  • 바인딩: “코드-이메일-회원” 강한 연결(사용자 입력 이메일로 회원 조회 금지, 코드가 가진 이메일로만 조회).
  • 감사: verified_at 기록 및 보안 감사 로그.

1-1: 기능 게이팅/UX 영향 점검

기본값이 미인증으로 바뀌면:

  • 로그인/핵심 기능 앞단에서 is_verified 체크가 누락되면 보안 구멍 또는 UX 혼선 발생.
  • 이메일 재전송/코드 재발급 흐름, 프론트 문구, API 스펙 업데이트 필요.
  • 모니터링: 인증 성공률, 재발송율, 실패 사유(만료/오입력) 대시보드 추가.

1-1: 서브모듈 소스 신뢰성 점검(.gitmodules 원격, 고정성)

서브모듈이 조직 공식 리포지터리 원격을 가리키는지, 포인터가 보호된 브랜치/태그에서 온 커밋인지 확인해 주세요. 재현성 확보를 위해 태그 고정 또는 릴리스 브랜치 방식을 고려하십시오.

src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (5)

93-95: 이메일 비교 시 대소문자/정규화 고려

시스템 전반에서 이메일을 대소문자 구분 없이 취급한다면 여기서도 동일하게 처리해야 합니다. 최소한 비교는 equalsIgnoreCase로, 더 나아가 저장 시/조회 시 모두 소문자 정규화를 권장합니다.

-        if (!request.email().equals(authCode.getEmail())) {
+        if (!request.email().equalsIgnoreCase(authCode.getEmail())) {
             throw new BusinessException(ExceptionType.AUTH_CODE_INVALID);
         }

101-101: 성공 시 해당 이메일의 잔여 인증 코드도 정리 고려

동일 이메일로 다중 코드가 발급된 경우, 검증 성공 후 사용된 코드만 삭제하면 잔여 코드가 남습니다(데이터 누적/예상치 못한 재사용 가능성). 다음과 같이 이메일 기준 일괄 삭제(또는 TTL/만료 컬럼 기반 정리)를 검토해 주세요.

-        authCodeRepository.deleteById(authCode.getCode());
+        authCodeRepository.deleteById(authCode.getCode());
+        // 선택: 동일 이메일의 잔여 코드 정리 (리포지토리 메서드 추가 필요)
+        // authCodeRepository.deleteAllByEmail(authCode.getEmail());

45-54: 메일 발송을 트랜잭션 커밋 이후로 이동

현재 트랜잭션 안에서 메일을 발송합니다. 커밋 실패 시 DB에는 코드가 없는데 사용자는 메일을 받은 상태(불일치)가 될 수 있습니다. 커밋 이후로 발송을 미루세요. 예시:

// 트랜잭션 내: 코드 생성/저장만 수행
AuthCode authCode = new AuthCode(code, email);
authCodeRepository.save(authCode);

// 커밋 이후 발송
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
    @Override public void afterCommit() {
        sendService.sendMail(email, subject, htmlContent);
    }
});

또는 도메인 이벤트 발행 + @TransactionalEventListener(phase = AFTER_COMMIT) 사용을 권장합니다.


56-66: SecureRandom.getInstanceStrong 잠재 블로킹 이슈

getInstanceStrong()는 환경에 따라 블로킹될 수 있습니다. 고가용성 요구 시 비블로킹 new SecureRandom()를 캐시해 사용하는 편이 안전합니다.

-            Random random = SecureRandom.getInstanceStrong();
+            SecureRandom random = SECURE_RANDOM; // 클래스 정적 필드로 캐시

클래스 상단(필드 영역)에 다음 필드를 추가하세요:

private static final SecureRandom SECURE_RANDOM = new SecureRandom();

88-102: 정책 확인: 미인증 회원의 로그인 허용 여부

이번 PR로 회원 기본값이 ‘미인증’이 됩니다. 현재 AuthService.signIn은 isVerified 체크가 없습니다(제공된 스니펫 참조). 의도대로 “인증 완료 전 로그인 제한” 정책이라면, 로그인 시 검증 여부를 확인하도록 변경이 필요합니다. 정책이 “로그인은 허용, 일부 기능 제한”이라면 현 상태로도 무방합니다. 확인 부탁드립니다. 필요 시 수정안 제안 가능합니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c907a21 and 9dca5f7.

📒 Files selected for processing (4)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (3 hunks)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/member/domain/Member.java (1 hunks)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java (0 hunks)
  • src/main/resources/properties (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

⚙️ CodeRabbit configuration file

**/*.java: This project follows key rules from the NAVER Campus Hackday Java Coding Conventions (partial summary below; see full spec for details). * Indentation: use spaces (no tabs), tab width = 4 spaces. Files must end with Unix LF newline. * Line length: maximum 120 characters. * Imports: single-class imports only, allow wildcard for static imports, group imports with blank lines between sections. * Assignment operators (=, +=, -=, etc.): always have one space before and after; when breaking lines, the operator must start the next line (Binary expressions: operation sign on next line). * Lambda expressions: omit parentheses for a single parameter; surround -> with spaces (param -> expression); use braces and explicit return for multi-statement bodies; choose short, clear parameter names to keep intent visible. For the complete set of conventions and detailed examples, please refer to the full spec at https://naver.github.io/hackday-conventions-java/.

Files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/member/domain/Member.java
🧠 Learnings (1)
📚 Learning: 2025-05-12T01:00:17.875Z
Learnt from: patulus
PR: th-D138/Backend-JobForeigner#32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java:64-67
Timestamp: 2025-05-12T01:00:17.875Z
Learning: AuthEmailService는 회원 검증과 이메일 인증 관련 비즈니스 예외(MEMBER_NOT_FOUND, EMAIL_ALREADY_VERIFIED, AUTH_CODE_INVALID)를 발생시키며, 이 예외들은 호출 서비스로 전파된다.

Applied to files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
🧬 Code graph analysis (1)
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (4)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/AuthService.java (2)
  • changeEmail (71-83)
  • AuthService (21-84)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java (1)
  • CompanyMemberService (19-62)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/ForeignerMemberService.java (1)
  • ForeignerMemberService (18-52)
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/controller/AuthEmailController.java (1)
  • verify (29-33)
🔇 Additional comments (4)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/domain/Member.java (1)

77-102: Builder 경유 생성 시 기본값 유지 의도 충족 — LGTM.

생성자 파라미터에 isVerified가 없어서 필드 초기값(false)이 그대로 유지됩니다. PR 목적과 일치합니다.

src/main/resources/properties (2)

1-1: 가입 서비스 반영 범위 재확인(외국인 회원 포함 여부)

PR 설명에는 CompanyMemberService와 ForeignerMemberService 모두가 기본값을 존중하도록 수정했다고 되어 있습니다. 실제로 ForeignerMemberService에서도 verified 조작이 제거/불가하도록 반영됐는지 확인해 주세요. 누락 시 신규 외국인 회원이 오인증 상태로 생성될 수 있습니다.


1-1: 서브모듈 클론 실패 — 의도한 변경사항 수동 확인 필요
src/main/resources/properties 서브모듈을 https://github.com/th-D138/Security-Backend-JobForeigner.git에서 클론할 수 없어 자동 검증이 불가능합니다. 서브모듈 원격 URL이 올바른지 확인하고, 커밋 38ec49702a8ccad0c17c10ef20f6e2143c7e3ff5에 이메일 인증 흐름 관련 파일 변경이 의도대로 반영되었는지 직접 검증해주세요.

src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (1)

10-12: MemberRepository 주입 추가 LGTM

이메일 인증 후 회원 상태 갱신을 위해 필요한 의존성 추가가 적절합니다. 생성자 주입(@requiredargsconstructor)과도 일관됩니다.

Also applies to: 34-34

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
.coderabbit.yaml (5)

33-46: path_filters에 빌드/래퍼 디렉터리 몇 가지를 추가 배제 권장

Gradle/Maven 래퍼 등 일부 생성물 경로가 누락되었습니다. 노이즈 감소용으로 다음 제외를 제안드립니다.

     - "!**/.idea/**"
     - "!**/.vscode/**"
+    - "!**/.mvn/**"
+    - "!gradle/wrapper/**"
+    - "!**/.venv/**"

증거: .coderabbit.yaml Line 33-46


62-74: Java 21 컨벤션 전반 양호하나 var+null 예외는 제거 권장

"Allow the var keyword when the value is a cast null"은 타입 가독성을 저하시킬 수 있고 정적분석 경고를 유발할 수 있습니다. 삭제를 제안드립니다.

         - Do not annotate immutable local variables with `final` unless required for an inner class.
-        - Allow the `var` keyword when the value is a cast `null`.
         - For the complete NAVER Campus Hackday conventions, see: https://naver.github.io/hackday-conventions-java/

증거: .coderabbit.yaml Line 71-74


76-87: 메인 소스 리뷰 지침 중 표준 출력 사용 권고는 변경 권장

프로덕션 코드에서 System.out.println 사용은 로깅 레벨/포맷/비동기 처리/성능/관측성 측면에서 비권장입니다. SLF4J 등 로깅 프레임워크 사용으로 수정하시길 권합니다.

-        - Use `System.out.println` instead of logging frameworks.
+        - Prefer a logging framework (e.g., SLF4J with Logback) over `System.out.println`.

증거: .coderabbit.yaml Line 85


104-169: 도구 설정 점검 — JS/TS 사용 여부, 취약점 스캐너 추가 제안

  • ESLint가 활성화되어 있습니다. 리포지토리에 JS/TS가 없다면 비활성화를 권장합니다(노이즈/체크 시간 감소).
  • OSV 기반 취약점 스캔을 사용하지 않는 상태입니다. 최소한의 종속성 취약점 탐지 강화를 위해 osvScanner 활성화를 제안드립니다.
  • PMD/Semgrep는 활성화가 적절합니다. 별도 규칙 파일이 있다면 pmd.config_file 또는 semgrep.config_file 지정도 고려해주세요.
   eslint:
-    enabled: true
+    enabled: false
@@
+  osvScanner:
+    enabled: true

증거: .coderabbit.yaml Line 147-147 (ESLint), Line 161-161 (Semgrep), Line 151-158 (PMD), Line 104-169 (tools 블록 전체)


174-192: 지식베이스 코드 가이드라인 파일 경로 지정 제안

code_guidelines.enabled: true이지만 filePatterns가 비어 있습니다. 조직 규칙 문서 경로를 명시하면 리뷰 일관성이 더 좋아집니다.

 knowledge_base:
@@
   code_guidelines:
     enabled: true
-    filePatterns: [ ]
+    filePatterns:
+      - ".github/instructions/*.instructions.md"
+      - "docs/**/CODE_STYLE*.md"
+      - "**/.cursorrules"
+      - "**/.windsurfrules"

증거: .coderabbit.yaml Line 177-180

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9dca5f7 and ba52a92.

📒 Files selected for processing (1)
  • .coderabbit.yaml (1 hunks)
🔇 Additional comments (8)
.coderabbit.yaml (8)

2-3: 언어/톤 설정 적절합니다

ko-KR 및 간결한 톤 지침이 팀 컨벤션과 일치합니다. 추가 조치 불필요합니다.


11-17: 리뷰 플래그 구성 적절 — 불필요한 산출 최소화에 도움

요약/워크스루/라벨 자동화 비활성 선택이 현재 팀 운영(리뷰어 수동 관리)에 맞습니다. 변경 필요 없어 보입니다.

Also applies to: 24-31


50-54: 일반 가이드 좋습니다 — 증거 요구·@토큰 백틱 처리

리뷰 일관성 제고에 유용합니다. 유지 권장합니다.


56-60: SQL 규칙 확인 필요 — bigint 강제의 DB 호환성

모든 PK/FK를 bigint로 강제하면 일부 DB/마이그레이션 도구에서 시퀀스/자동증가 타입 매핑이 달라질 수 있습니다(예: PostgreSQL bigserial, MySQL BIGINT AUTO_INCREMENT). 실제 사용 DB/DDL 전략과의 호환성 확인을 부탁드립니다.

증거: .coderabbit.yaml Line 56-60


88-92: 테스트 코드 지침 타당

정확성/엣지케이스를 우선시하는 방향이 적절합니다.


94-94: disable_cache=false 유지 합리적

리뷰 속도/비용 균형에 적절합니다. 캐시 무효화가 필요할 때만 임시로 true 권장.


99-103: auto_review 세부 설정 무난

드래프트 미검토, 라벨/베이스브랜치 필터 비적용 모두 현재 워크플로우에 적합해 보입니다.


170-172: chat.auto_reply 설정 무난

프로젝트 커뮤니케이션 흐름에 맞습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (1)

62-62: 만료(Expiry)/재시도 제한 로직 추가 필요
AuthCode 엔티티에 생성 시각(createdAt) 및 만료 시각(expiryAt) 필드를 추가하고, AuthCodeRepository에 만료된 코드 정리·유효 코드 조회 메서드를 정의한 뒤, AuthEmailService(62, 78–83, 94–108)에서 코드 검증 시 TTL 확인 및 재발송 횟수 제한 로직을 구현해주세요.

♻️ Duplicate comments (1)
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (1)

103-106: 이미 인증된 계정 처리 누락 — 기존 계약(EMAIL_ALREADY_VERIFIED) 회귀 위험

  • 파일: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • 라인: 103-106
  • 이슈: verifyEmail에서 회원을 찾은 뒤 무조건 setVerified()를 호출합니다. 장기 메모에 따른 계약상 이미 인증된 경우 EMAIL_ALREADY_VERIFIED를 던져야 합니다.
  • 증빙: Retrieved Learnings(이전 PR #32) — AuthEmailService의 예외 계약(MEMBER_NOT_FOUND, EMAIL_ALREADY_VERIFIED, AUTH_CODE_INVALID).
  • 조치: 아래와 같이 가드를 추가하고, 코드 재사용 방지를 위해 예외 전 코드 삭제까지 권장합니다.
         Member member = memberRepository.findByEmail(request.email())
                 .orElseThrow(() -> new BusinessException(ExceptionType.MEMBER_NOT_FOUND));
-        member.setVerified();
+        if (member.isVerified()) {
+            // 사용된 인증 코드는 더 이상 유효하지 않도록 즉시 폐기
+            authCodeRepository.deleteById(authCode.getCode());
+            throw new BusinessException(ExceptionType.EMAIL_ALREADY_VERIFIED);
+        }
+        member.setVerified();
🧹 Nitpick comments (4)
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (4)

47-48: 회원 존재 여부 노출로 인한 사용자 열거(User Enumeration) 가능성

  • 파일: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • 라인: 47-48
  • 이슈: MEMBER_NOT_FOUND를 직접 던지면 이메일 존재 여부가 엔드유저에게 노출될 수 있습니다. 인증 메일 발송 API 특성상 동일 응답을 반환하도록 마스킹하는 것이 안전합니다.
  • 제안: 존재하지 않으면 no-op 후 성공 응답 처리(컨트롤러 레벨 계약과 합의 필요). 또는 동일 에러 메시지 사용.
  • 참고: OWASP ASVS(Authentication) — 사용자 열거 방지 원칙
-        Member member = memberRepository.findByEmail(email)
-            .orElseThrow(() -> new BusinessException(ExceptionType.MEMBER_NOT_FOUND));
+        Member member = memberRepository.findByEmail(email).orElse(null);
+        if (member == null) {
+            // 존재 여부를 노출하지 않기 위해 조용히 반환
+            return;
+        }

57-59: 인증 코드 중복/다중 발급 정책 — 1인 1활성 코드 권장

  • 파일: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • 라인: 57-59
  • 이슈: 동일 이메일에 대해 여러 코드가 동시에 유효해질 수 있습니다. 보안/UX 측면에서 최근 발급 1건만 유효하도록 이전 코드를 무효화하는 정책을 권장합니다.
  • 제안: 저장 전 기존 코드 삭제(Repository에 deleteByEmail(String) 추가 필요).
-        AuthCode authCode = new AuthCode(code, email);
+        // 기존 코드를 제거하여 1인 1활성 코드 정책 보장
+        authCodeRepository.deleteByEmail(email);
+        AuthCode authCode = new AuthCode(code, email);
         authCodeRepository.save(authCode);

62-76: SecureRandom.getInstanceStrong 잠재 블로킹 — 경량 인스턴스의 재사용 권장

  • 파일: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • 라인: 62-76
  • 이슈: SecureRandom.getInstanceStrong()는 플랫폼에 따라 블로킹될 수 있습니다. 요청당 생성도 비용이 큽니다.
  • 제안: 클래스 정적 필드로 new SecureRandom() 한 번 생성 후 재사용. 6자리 숫자 코드 용도에 적합.
  • 문서: Java 21 SecureRandom javadoc(강한 인스턴스는 블로킹 가능성 명시)
-    private String createCode() {
-        try {
-            Random random = SecureRandom.getInstanceStrong();
+    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
+
+    private String createCode() {
+        try {
-            StringBuilder builder = new StringBuilder();
+            StringBuilder builder = new StringBuilder(LENGTH);
             for (int i = 0; i < LENGTH; ++i) {
-                builder.append(random.nextInt(10));
+                builder.append(SECURE_RANDOM.nextInt(10));
             }
             return builder.toString();
         } catch (NoSuchAlgorithmException e) {
             System.out.println("이메일 주소 인증 메일의 인증 코드 생성에 실패했습니다: " + e.getMessage());
             throw new BusinessException(ExceptionType.UNEXPECTED_SERVER_ERROR);
         }
     }

84-87: 템플릿 로더/컴파일 매 호출 생성 비용 — 빈으로 재사용 또는 프리컴파일 권장

  • 파일: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • 라인: 84-87
  • 이슈: MustacheResourceTemplateLoader 생성 및 템플릿 컴파일을 매번 수행하면 요청당 비용이 큽니다.
  • 제안: 스프링 빈으로 로더를 주입하거나, 템플릿을 애플리케이션 시작 시 컴파일/캐시하여 재사용.
-        try (Reader reader = new MustacheResourceTemplateLoader("templates/", ".html")
-                .getTemplate("email/auth/auth-mail")) {
+        try (Reader reader = mustacheTemplateLoader.getTemplate("email/auth/auth-mail")) {
             Template template = Mustache.compiler().compile(reader);
             return template.execute(model);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba52a92 and 07db1c7.

📒 Files selected for processing (1)
  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*

⚙️ CodeRabbit configuration file

**/*: - For every finding, include evidence: file path(s), line number(s), rule names (e.g., Checkstyle), and links to credible docs when relevant.

  • Wrap any tokens starting with '@' (e.g., @JoinColumn) in backticks in PR comments.

Files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
**/*.java

⚙️ CodeRabbit configuration file

**/*.java: - Review against Java 21.

  • Indentation: use spaces (no tabs), tab width = 4 spaces; files must end with Unix LF newline. (Team adaptation)
  • Maximum line length: 120 characters.
  • Imports: single-class imports only; allow wildcard for static imports; group imports with blank lines between sections.
  • Assignment operators (=, +=, -=, etc.): always one space before and after; on line breaks, place the operator at the start of the next line.
  • Lambda expressions: omit parentheses for a single parameter; surround -> with spaces (param -> expression); use braces and explicit return for multi-statement bodies; choose short, clear parameter names.
  • In multi-line expressions, place operators and ternary separators at end-of-line.
  • Prefer Java 21 standard APIs over Guava.
  • Do not annotate immutable local variables with final unless required for an inner class.
  • Allow the var keyword when the value is a cast null.
  • For the complete NAVER Campus Hackday conventions, see: https://naver.github.io/hackday-conventions-java/

Files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
**/main/java/**/*.java

⚙️ CodeRabbit configuration file

**/main/java/**/*.java: - This project is mature and must preserve a stable, backward-compatible public Java API.

  • In the "Changes" section, list up to 25 changes to the public Java API that could affect end users; if none, explicitly state "No public Java API changes in this PR."
  • Define the public Java API as public/protected methods on public classes plus module-info.java.
  • Derive the list by deeply analyzing code flow, including through private methods and calls to Java 21 and Guava.
  • Report:
    • New or removed public API methods
    • Changes to return types or parameter types
    • Behavioral changes that may impact consumers
  • Use System.out.println instead of logging frameworks.
  • For performance reasons, the project prefers for-loops; do not suggest converting between loops and streams.

Files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
🧠 Learnings (2)
📚 Learning: 2025-05-12T01:00:17.875Z
Learnt from: patulus
PR: th-D138/Backend-JobForeigner#32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java:64-67
Timestamp: 2025-05-12T01:00:17.875Z
Learning: AuthEmailService는 회원 검증과 이메일 인증 관련 비즈니스 예외(MEMBER_NOT_FOUND, EMAIL_ALREADY_VERIFIED, AUTH_CODE_INVALID)를 발생시키며, 이 예외들은 호출 서비스로 전파된다.

Applied to files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
📚 Learning: 2025-05-12T01:23:43.096Z
Learnt from: patulus
PR: th-D138/Backend-JobForeigner#32
File: src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java:0-0
Timestamp: 2025-05-12T01:23:43.096Z
Learning: 이메일 전송(EmailSendService.sendMail)은 비동기(Async)로 실행되며 내부적으로 예외를 처리하므로, 이메일 전송 실패가 회원 등록 트랜잭션 완료에 영향을 주지 않는다. 이메일을 받지 못한 사용자는 재전송 API를 통해 인증 메일을 다시 요청할 수 있다.

Applied to files:

  • src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
🧬 Code graph analysis (1)
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (4)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/AuthService.java (2)
  • changeEmail (71-83)
  • AuthService (21-84)
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/controller/AuthEmailController.java (1)
  • AuthEmailController (17-34)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/ForeignerMemberService.java (1)
  • ForeignerMemberService (18-52)
src/main/java/kr/ac/kumoh/d138/JobForeigner/member/service/CompanyMemberService.java (1)
  • CompanyMemberService (19-62)
🔇 Additional comments (3)
src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java (3)

1-110: Public Java API 변경 사항(Changes) — 없음

  • 파일: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • 라인: 1-110
  • 결론: 외부에 노출되는 public/protected 메서드 시그니처 변경 없음. sendMail(String), verifyEmail(VerifyAuthCodeRequest) 유지. @RequiredArgsConstructor로 생성자 파라미터 구성은 바뀌었으나(Spring DI 대상) 공개 API 안정성에는 영향 없음.

10-12: MemberRepository 주입 추가 — DI 구성 적합

  • 파일: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • 라인: 10-12, 34
  • 설명: 단일 클래스 임포트와 임포트 그룹 간 공백 유지로 팀 컨벤션 준수. 생성자 주입(@RequiredArgsConstructor)과 일관.
  • 레퍼런스: Team Java 컨벤션(임포트 섹션) https://naver.github.io/hackday-conventions-java/

Also applies to: 34-34


49-51: 이미 인증된 계정에 대한 가드 추가 — 재발송 차단 적절

  • 파일: src/main/java/kr/ac/kumoh/d138/JobForeigner/email/service/AuthEmailService.java
  • 라인: 49-51
  • 설명: EMAIL_ALREADY_VERIFIED 가드를 통해 인증된 이메일로의 재발송을 차단한 것은 계약 준수이며 회귀를 방지합니다. 장기 러닝과 일치합니다.

Copy link
Contributor

@kon28289 kon28289 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다!

Copy link
Contributor

@Juhye0k Juhye0k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다

@patulus patulus merged commit e319f64 into dev Sep 9, 2025
3 checks passed
@patulus patulus deleted the feat/verify-email branch September 9, 2025 11:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants